/*
 * ArenaTicketMain - Venda de Bilhetes Eletronicos para Espetaculos (Arena Entretenimento S.A.)
 * CompraDAOMySQL.java
 * ©2006
 */
package br.unicamp.ic.inf312.arena.persistencia.dao.impl;

import java.sql.Date;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;

import br.unicamp.ic.inf312.arena.modelo.artistico.Espetaculo;
import br.unicamp.ic.inf312.arena.modelo.logistico.Evento;
import br.unicamp.ic.inf312.arena.modelo.vendas.Bilhete;
import br.unicamp.ic.inf312.arena.modelo.vendas.CartaoCredito;
import br.unicamp.ic.inf312.arena.modelo.vendas.Compra;
import br.unicamp.ic.inf312.arena.modelo.vendas.Estorno;
import br.unicamp.ic.inf312.arena.modelo.vendas.Pagamento;
import br.unicamp.ic.inf312.arena.modelo.vendas.ValeEntrada;
import br.unicamp.ic.inf312.arena.persistencia.dao.IBilheteDAO;
import br.unicamp.ic.inf312.arena.persistencia.dao.ICompraDAO;
import br.unicamp.ic.inf312.arena.persistencia.dao.IPagamentoDAO;
import br.unicamp.ic.inf312.arena.util.Util;
import br.unicamp.ic.inf312.framework.persistencia.DataAccessObjectFactory;
import br.unicamp.ic.inf312.framework.persistencia.SQLUtil;
import br.unicamp.ic.inf312.framework.util.FrameworkUtil;

/**
 * @author  inf312
 */
public class CompraDAOMySQL implements ICompraDAO {

	/**
	 * obter
	 */
	public Compra obter(long numProtocolo) throws Exception {

		Compra compra = null;
		Estorno estorno = null;
		ValeEntrada vale = null;
		CartaoCredito cartao = null;
		try {
			StringBuffer stbQuery = new StringBuffer();
			stbQuery.append(" SELECT ");
			//                  1               2           3       4
			stbQuery.append("   c.numprotocolo, c.dataHora, p.tipo, p.codpagto, ");
			//                  5        6               7           8
			stbQuery.append("   p.valor, p.numerocartao, p.bandeira, p.validade, ");
			//                  9
			stbQuery.append("   p.codestorno ");
			stbQuery.append(" FROM ");
			stbQuery.
					append("    compra c, pagamento p ");
			stbQuery.append(" WHERE ");
			stbQuery.append("   c.codpagto     = p.codpagto AND ");
			stbQuery.append("   c.numprotocolo = ? ");
			//
            Object[] params = new Object[1];
            params[0] = numProtocolo;

            Object[] record = (Object[]) SQLUtil.selectARecord(stbQuery
					.toString(), params);

    		if (record != null && record.length > 0) {
				compra = new Compra();
				compra.setNumProtocolo( ((Long) record[0]).longValue() );
				compra.setDataHora( ((Timestamp) record[1]) );

				//
				int tipo = 0;
				if (record[2] != null) {
					tipo = ((Integer) record[2]).intValue();
				}
				//
				int codpagto = 0;
				if (record[3] != null) {
					codpagto = ((Integer) record[3]).intValue();
				}
				//
				double valor = 0.00D;
				if (record[4] != null) {
					valor = ((Double) record[4]).doubleValue();
				}
				//
				long numeroCartao = 0L;
				if (record[5] != null) {
					numeroCartao = ((Long) record[5]).longValue();
				}
				//
				String strBandeira = "";
				if (record[6] != null) {
					strBandeira = ((String) record[6]);
				}
				//
				Date validade = null;
				if (record[7] != null) {
					validade = (java.sql.Date) record[7];
				}
				//
				long codEstorno = 0L;
				if (record[8] != null) {
					codEstorno = ((Long) record[8]).longValue();
				}

				//instancia subclasse de Pagamento
				if (tipo == Pagamento.TIPO_CARTAO) {
					cartao = new CartaoCredito(codpagto, valor, numeroCartao,
							strBandeira);
					compra.setPagamento(cartao);

				} else if (tipo == Pagamento.TIPO_VALE_ENTRADA) {
					vale = new ValeEntrada(codpagto, valor, validade,
							codEstorno);
					compra.setPagamento(vale);
				}
				
				//TODO: Alteracao para a ativ. 11
				this.carregarBilhetes(compra);
			}
		} catch (SQLException sql) {
			System.err.println(sql.getMessage());
			throw new Exception(sql.getMessage());
		}
		return compra;
	}

	/**
	 * 
	 */
	public boolean inserir(Timestamp dataHora, int codPagto)  throws Exception {
		int result = 0;
		Object[] params = new Object[2];
		params[0] = codPagto;
		params[1] = dataHora;

		StringBuffer stbQuery = new StringBuffer();
		stbQuery.append(" INSERT INTO ");
		stbQuery.append(SQLUtil.getDbSchema() + "."
				+ ICompraDAO.TABELA_BD + " (");
		stbQuery.append(ICompraDAO.COLUMN_CODIGO_PAGTO + ", ");
		stbQuery.append(ICompraDAO.COLUMN_DATA_HORA + ") ");
		stbQuery.append(" VALUES (?, ?) ");
		//
		result = SQLUtil.update(stbQuery.toString(), params);
		if (result <= 0) {
			throw new Exception("Falha na gravacao da compra!");
		}		
		return true;
	}
	
	/**
	 * inserir
	 */
	public boolean inserir(Compra compra)  throws Exception {
		int result = 0;
		ValeEntrada vale = null;
		CartaoCredito cartao = null;

		if (compra != null) {
			//-- 1o. faz insert de pagamento na base
			if (compra.getPagamento() == null) {
				throw new Exception("Compra nao cadastrada, pagamento esta nulo!");
			}

			IPagamentoDAO daoPagto = (IPagamentoDAO) DataAccessObjectFactory
					.getImpl(IPagamentoDAO.class);

			boolean insertOK = false;
			if (compra.getPagamento().getTipo() == Pagamento.TIPO_CARTAO) {
				cartao = (CartaoCredito) compra.getPagamento();
				//INSERT especifico p/ colunas de pagamento qdo este eh por cartao
				insertOK = daoPagto.inserir(cartao);
			}
			else if (compra.getPagamento().getTipo() == Pagamento.TIPO_VALE_ENTRADA) {
				vale = (ValeEntrada) compra.getPagamento();
				//INSERT especifico p/ colunas de pagamento qdo este eh por vale
				insertOK = daoPagto.inserir(vale);
			}

			//-- 2o. obtem col. codpagto gerada p/ gravar Compra
			if (insertOK) {
				compra.getPagamento().setCodpagto( daoPagto.obterCodPagto() );
			}

			//-- 3o. gravar Compra
			Object[] params = new Object[2];
			params[0] = compra.getPagamento().getCodpagto();
			//
			if ( compra.getDataHora() == null) {
				params[1] = FrameworkUtil.obterDataCorrenteSQL();
			} else {
				params[1] = compra.getDataHora(); 
			}

			StringBuffer stbQuery = new StringBuffer();
			stbQuery.append(" INSERT INTO ");
			stbQuery.append(SQLUtil.getDbSchema() + "."
					+ ICompraDAO.TABELA_BD + " (");
			stbQuery.append(ICompraDAO.COLUMN_CODIGO_PAGTO + ", ");
			stbQuery.append(ICompraDAO.COLUMN_DATA_HORA + ") ");
			stbQuery.append(" VALUES (?, ?) ");
			//
			result = SQLUtil.update(stbQuery.toString(), params);
			if (result <= 0) {
				throw new Exception("Falha na gravacao da compra!");
			}

			//-- 4o. gravar Bilhete(s) da Compra
			if (compra.getBilhetes() == null) {
				throw new Exception("Compra mal formulada, nao possui bilhete associado!");
			}

			IBilheteDAO daoBilhete = null;
			daoBilhete = (IBilheteDAO) DataAccessObjectFactory
					.getImpl(IBilheteDAO.class);

			for (Bilhete bilhete : compra.getBilhetes()) {
				daoBilhete.insert(bilhete);
			}
		}
		return (result > 0 ? true : false);
	}
	
	/**
	 * 
	 * @return long
	 * @throws Exception
	 */
	public synchronized long obterProximoNumeroProtocolo() throws Exception {
		int result = 0;
		try {
			StringBuffer stbQuery = new StringBuffer();
			stbQuery.append(" SELECT MAX(" + ICompraDAO.COLUMN_CODIGO_PK
					+ ") FROM " + SQLUtil.getDbSchema() + "." + ICompraDAO.TABELA_BD);

			result = SQLUtil.selectCount(stbQuery.toString());
		} catch (SQLException e) {
			throw new Exception(e.getLocalizedMessage());
		}
		return (++result);
	}
	
	//TODO: Alteracao para a ativ. 11
	private void carregarBilhetes( Compra compra ) throws Exception
	{
		// obtem o DAO de bilhetes
		IBilheteDAO bilheteDAO = (IBilheteDAO) DataAccessObjectFactory.getImpl( IBilheteDAO.class );
		
		// obtem os bilhetes pertencentes a compra
		List<Bilhete> bilhetes = (List<Bilhete>) bilheteDAO.obterBilhetes( compra.getNumProtocolo() );
		
		// certifica que a compra possui bilhetes
		if ( bilhetes.size() > 0 )
		{
			// obtem as informacoes (parciais) do evento associado aos bilhetes
			Evento evento = bilhetes.get(0).getEvento();
			
			//debuh..
			// obtem o codigo do espetaculo associado ao evento
			Object[] codEspetaculo = (Object[]) SQLUtil.selectARecord( "SELECT codespetaculo FROM evento WHERE codevento = ?", 
								new Object[]{evento.getCodEvento()} );
			
			// carrega o espetaculo associado ao evento
			Espetaculo espetaculo = (Espetaculo) SQLUtil.retrieveBean( "SELECT * FROM espetaculo WHERE codespetaculo = ?", Espetaculo.class, codEspetaculo );
			
			// carrega as outras informacoes do evento
			evento = (Evento) SQLUtil.retrieveBean( "SELECT * FROM evento WHERE codevento = ?", Evento.class, new Object[]{evento.getCodEvento()} );
			
			// associa o espetaculo ao evento
			evento.setEspetaculo( espetaculo );
			
			// associa o evento aos bilhetes
			for ( Bilhete bilhete : bilhetes )
				bilhete.setEvento( evento );
		}
		
		// associa os bilhetes a compra
		compra.setBilhetes( bilhetes );
	}
	
	//TODO: Alteracao para a ativ. 11
	// PASSAR ESSA PORRA P/ O TransacaoCompraGerenciador!!!!
	public Estorno devolver(Compra compra, List<Bilhete> bilhetes) throws Exception
	{
		//popula as informacoes de estorno
		Estorno estorno = new Estorno();
		estorno.setBillhetes(bilhetes);
		estorno.setDataHora(SQLUtil.getCurrentTimestamp());
		estorno.setCompra(compra);

		//atualiza as informacoes dos bilhetes devolvidos
		double valorEstorno = 0.0;
		for ( Bilhete bilhete : bilhetes )
		{
			bilhete.setEstado(Bilhete.ESTADO_DEVOLVIDO);
			bilhete.setEstorno(estorno);
			valorEstorno += bilhete.getValor();
		}
		
		//monta o SQL p/ insercao do estorno
		StringBuilder strQuery = new StringBuilder("INSERT INTO estorno(datahora, valor, numerocartao, bandeira, numprotocolo) ");
		strQuery.append("VALUES(?, ?, ?, ?, ?)");
		Object[] parametros = new Object[5];
		
		//define os parametros p/ o estorno
		parametros[0] = estorno.getDataHora();
		parametros[1] = valorEstorno;
		parametros[2] = ((CartaoCredito) compra.getPagamento()).getNumeroCartao();
		parametros[3] = ((CartaoCredito) compra.getPagamento()).getBandeira();
		parametros[4] = compra.getNumProtocolo();

		//insere o estorno no BD
		SQLUtil.update(strQuery.toString(), parametros);
		
		//recupera o numero do estorno (gerado automaticamente pelo BD)
		int numEstorno = SQLUtil.selectCount("SELECT MAX(numero) FROM estorno");
		estorno.setNumero(numEstorno);
		
		//monta o SQL p/ atualizacao do estado do bihete estornado
		strQuery = new StringBuilder("UPDATE bilhete SET estado = ?, numeroEstorno = ? WHERE numero = ?");
		
		//define os parametros p/ o bilhete
		parametros = new Object[3];
		parametros[0] = Bilhete.ESTADO_DEVOLVIDO;
		parametros[1] = numEstorno;
		
		//atualiza os bilhetes no BD
		for ( Bilhete bilhete : bilhetes )
		{
			parametros[2] = bilhete.getNumero();
			SQLUtil.update(strQuery.toString(), parametros);
		}
		
		//monta o SQL p/ atualizacao do pagamento no BD, inserindo o cod. do estorno 
		strQuery = new StringBuilder("UPDATE pagamento SET codestorno = ? WHERE codpagto = ?");
		
		//define os parametros p/ o pagamento
		parametros = new Object[2];
		parametros[0] = estorno.getNumero();
		parametros[1] = compra.getPagamento().getCodpagto();
		
		//atualiza o estado do pagamento no BD com o codigo do estorno
		SQLUtil.update(strQuery.toString(), parametros);
		
		//debitar o valor do estorno da conta bancaria do estabelecimento
		
		//verifica se o tempo do estorno esta dentro do limite da operadora de cartao de credito
		
		//se nao estiver, creditar o valor do estorno na conta bancaria do comprador 
		
		//retorna o estorno
		return ( estorno );
	}
}